package linuxbox.reservation.logic;

import java.util.*;
import org.apache.log4j.*;

import linuxbox.bean.*;
import linuxbox.util.*;

import linuxbox.reservation.bean.*;
import linuxbox.resource.logic.*;


public class BoxReservation
{
  protected static Logger logger = Logger.getLogger(BoxReservation.class.getName());
  protected static BoxResource resourceHome = BoxResource.getInstance();

  protected static BoxReservation staticInstance = null;

  public static BoxReservation getInstance()
  {
    if (staticInstance == null)
    {
      staticInstance = new BoxReservation();
    }
    return staticInstance;
  }

  private BoxReservation()
  {}

  public BoxReservationEntry get(BaseIntKey key)
  {
    BoxReservationEntry entry = null;
    try
    {
      BoxReservationEntryBean bean = new BoxReservationEntryBean(key);
      bean.load();
      entry = new BoxReservationEntry();
      entry.copyFrom(bean);
    }
    catch (Exception e)
    {
      //News: Log something?
      e.printStackTrace();
    }
    return entry;
  }

  public BoxReservationRepeatEntry getRepeat(BaseIntKey key)
  {
    BoxReservationRepeatEntry entry = null;
    try
    {
      BoxReservationRepeatBean bean = new BoxReservationRepeatBean(key);
      bean.load();
      entry = new BoxReservationRepeatEntry();
      entry.copyFrom(bean);
    }
    catch (Exception e)
    {
      //News: Log something?
      e.printStackTrace();
    }
    return entry;
  }

  public BoxReservationEntry add()
  {
    try
    {
      BoxReservationEntryBean bean = new BoxReservationEntryBean(new BaseIntKey(0));
      bean.create();

      BoxReservationEntry entry = new BoxReservationEntry();
      entry.copyFrom(bean);
      return entry;
    }
    catch (Exception e)
    {
      //TODO: Log something?
      e.printStackTrace();
    }
    return null;
  }

  public boolean remove(int id) throws Exception
  {
    return remove(new BaseIntKey(id), null);
  }

  public boolean remove(int id, DbAdapter db) throws Exception
  {
    return remove(new BaseIntKey(id), db);
  }

  public boolean remove(BaseIntKey key, DbAdapter db1) throws Exception
  {
    DbAdapter db=null;
    if (db1 == null)
      db = new DbAdapter(true);
    else
      db = db1;
    BoxReservationEntryBean bean = new BoxReservationEntryBean(key, db);
    bean.load();
    if (bean.getRepeat())
    {
      BoxReservationRepeatBean repeat = new BoxReservationRepeatBean(key, db);
      repeat.remove();
    }
    bean.remove();
    if (db1 == null)
      db.close();
    return true;
  }

  public boolean update(BoxReservationEntry entry, BoxReservationRepeatEntry repeat, TimeZone tz)
  {
logger.debug("Updating: ");
logger.debug(entry.toString());
logger.debug(repeat);
    if (hasConflict(entry, repeat, tz))
      return false;

    try
    {
      DbAdapter db = new DbAdapter(true);
      BaseIntKey key = new BaseIntKey(entry.getID());

      BoxReservationEntryBean bean = new BoxReservationEntryBean(key, db);
      bean.copyFrom(entry);
      //If you are not passing the repeat entry, I will set the repeat
      //to false, otherwise, it will look for a repeat bean which is not exist.
      if ( repeat == null )
        bean.setRepeat(false);
      bean.store();

      BoxReservationRepeatBean repeatBean = new BoxReservationRepeatBean(key,db);
      if ( bean.getRepeat() )
      {
        //The repeat bean's ID has to be the same as the entry
        if ( repeatBean.findByKey() == null )
        {
          repeatBean.create();
        }
        repeatBean.copyFrom(repeat);
        repeatBean.store();
      }
      else
      {
        repeatBean.remove();
      }
      db.close();
    }
    catch (Exception e)
    {
      //TODO: Log something?
      e.printStackTrace();
      return false;
    }
    return true;
  }

  public boolean hasConflict(BoxReservationEntry entry, BoxReservationRepeatEntry repeat, TimeZone tz)
  {
logger.debug("Check conflict with: ");
logger.debug(entry);
    if (!entry.getRepeat() || repeat == null)
    {
      entry.setRepeat(false);
      Calendar fromtime = Calendar.getInstance(tz);
      fromtime.setTime(entry.getFromTime());
      Calendar totime = Calendar.getInstance(tz);
      totime.setTime(entry.getToTime());
      return checkConflict(fromtime, totime, entry.getResourceID(), entry.getID());
    }
    else
    {
      return checkRepeatConflict(entry, repeat, tz);
    }
  }

  public String getResourceString(int resourceID)
  {
    BoxResourceEntry entry = resourceHome.get(new BaseIntKey(resourceID));
    if (entry != null)
      return entry.getName();
    return "";
  }

  public boolean hasReservationForResource(int resourceID) throws Exception
  {
     DbAdapter db = new DbAdapter(true);

     BoxReservationEntryBean bean = new BoxReservationEntryBean(null,db);
     String sql = " where resourceid=" + resourceID;
     Iterator it = bean.findBySql(sql);
     db.close();

     if (it != null && it.hasNext())
       return true;
    return false;
  }

  public void removeAllReservationForResource(int resourceID) throws Exception
  {
    DbAdapter db = new DbAdapter(true);

    BoxReservationEntryBean bean = new BoxReservationEntryBean(null,db);
    String sql = " where resourceid=" + resourceID;
    Iterator it = bean.findBySql(sql);

    if (it == null || !it.hasNext())
    {
      db.close();
      return;
    }
    while (it.hasNext())
    {
      BaseIntKey key = (BaseIntKey) it.next();
      remove(key, db);
    }
    db.close();
  }

  // return true if there is conflict
  protected boolean checkRepeatConflict(BoxReservationEntry entry, BoxReservationRepeatEntry repeat, TimeZone tz)
  {
    //1. get all the none repeat entries, check each against this repeat entry.
    StringBuffer sql = new StringBuffer();
    sql.append(" where resourceid=");
    sql.append(entry.getResourceID());
    sql.append(" and id<>");
    sql.append(entry.getID());
    sql.append(" and repeat");
    String repeatSql = sql.toString();
    repeatSql += "<>0";
    sql.append("=0");
    try
    {
      DbAdapter db = new DbAdapter(true);
      Iterator it = getBySql(sql.toString(), db);
      if (it != null && it.hasNext())
      {
        Calendar fromtime = Calendar.getInstance(tz);
        Calendar totime = Calendar.getInstance(tz);
        while (it.hasNext())
        {
          BoxReservationEntry tmpEntry = (BoxReservationEntry)it.next();
          fromtime.setTime(tmpEntry.getFromTime());
          totime.setTime(tmpEntry.getToTime());
          if (isRepeatInRange(entry, repeat, fromtime, totime))
          {
            db.close();
            return true;
          }
        }
      }

      //2. get all the repeat entries, check each against this repeat entry
      it = getRepeatBySql(repeatSql, db);
      if ( it != null && it.hasNext())
      {
        while (it.hasNext())
        {
          BoxReservationRepeatEntry repeat1 = (BoxReservationRepeatEntry) it.next();
          BoxReservationEntryBean entry1 = new BoxReservationEntryBean(new BaseIntKey(repeat1.getID()),db);
          entry1.load();

          if (isTwoRepeatConflict(entry, repeat, entry1, repeat1, tz))
          {
            db.close();
            return true;
          }
        }
      }

      db.close();
    }
    catch (Exception e)
    {
    }
/*
    Calendar fromtime = Calendar.getInstance(tz);
    fromtime.setTime(entry.getFromTime());
    Calendar totime = Calendar.getInstance(tz);
    totime.setTime(entry.getToTime());
    Calendar curDate = Calendar.getInstance(tz);
    curDate.setTime(entry.getFromTime());
    while (getNextRepeatRange(entry,repeat,curDate,fromtime,totime))
    {
      if (checkConflict(fromtime, totime, entry.getResourceID(), entry.getID()))
        return true;
    }
 */
    return false;
  }

  protected boolean isTwoRepeatConflict(BoxReservationEntry entry1, BoxReservationRepeatEntry repeat1,
                                        BoxReservationEntry entry2, BoxReservationRepeatEntry repeat2,
                                        TimeZone tz)
  {
    Calendar from1 = Calendar.getInstance(tz);
    from1.setTime(entry1.getFromTime());
    Calendar to1 = Calendar.getInstance(tz);
    to1.setTime(entry1.getToTime());
    Calendar from2 = Calendar.getInstance(tz);
    from2.setTime(entry2.getFromTime());
    Calendar to2 = Calendar.getInstance(tz);
    to2.setTime(entry2.getToTime());

    if (!isTimeRangeConflict(from1, to1, from2, to2))
      return false;

    //TODO: Optimize by checking repeat type
    Calendar curDate = Calendar.getInstance(tz);
    curDate.setTime(entry1.getFromTime());
    while (getNextRepeatRange(entry1,repeat1,curDate,from1,to1))
    {
      if (isRepeatInRange(entry2, repeat2, from1, to1))
        return true;
    }

    return false;
  }

/*
  public Iterator getResourceEntries(int resourceID)
  {
    String sql = " where resourceid = " + resourceID;
    return getBySql(sql,null);
  }

  public Iterator getEntries(String from, String to, String USERID, boolean VIEW_MINE, int RESOURCEID, int RESOURCECATID, String SORTBY)
  {
    StringBuffer sql = new StringBuffer();
    sql.append(" where (( fromtime >= '");
    sql.append(from);
    sql.append("' and fromtime < '");
    sql.append(to);
    sql.append("') or (totime >= '");
    sql.append(from);
    sql.append("' and totime < '");
    sql.append(to);
    sql.append("') or (fromtime <= '");
    sql.append(from);
    sql.append("' and totime >= '");
    sql.append(to);
    sql.append("'))");
    if ( VIEW_MINE )
    {
      sql.append(" and (reservedby='");
      sql.append(USERID);
      sql.append("')");
    }
    if ( RESOURCEID != -1)
    {
      sql.append(" and (resourceid=");
      sql.append(RESOURCEID);
      sql.append(")");
    }
    else if ( RESOURCECATID != -1)
    {
      Iterator it = resourceHome.getResourceByCat(RESOURCECATID);
      if ( it != null && it.hasNext())
      {
        sql.append(" and (resourceid=");
        BoxResourceEntry entry = (BoxResourceEntry)it.next();
        sql.append(entry.getID());
        while (it.hasNext())
        {
          entry = (BoxResourceEntry)it.next();
          sql.append(" or resourceid=");
          sql.append(entry.getID());
        }
        sql.append(")");
      }
      else
        return null;
    }
    String repeatSql = sql.toString();
    sql.append(" and (repeat=0)");
    if ( SORTBY != null && SORTBY.length()>0)
    {
      sql.append(" order by ");
      sql.append(SORTBY);
    }
    Iterator it = getBySql(sql.toString(),null);
    Vector v = new Vector();
    if ( it!= null && it.hasNext())
    {
      while (it.hasNext())
      {
        BoxReservationEntry entry = (BoxReservationEntry) it.next();
        if (SORTBY.compareToIgnoreCase("fromtime") != 0 )
          v.addElement(entry);
        else
          insertToSortedVector(entry, v, SORTBY);
      }
    }
    //Now check the repeat entries
    return v.iterator();
  }
*/
  public Iterator getEntries(Calendar fromTime, Calendar toTime, String USERID, boolean VIEW_MINE, int RESOURCEID, String SORTBY, boolean sortAsc)
  {
    if ( RESOURCEID == -1)
      return getEntries(fromTime, toTime, USERID, VIEW_MINE, null, SORTBY, sortAsc, -1);
    else
    {
      int resource[] = new int [1];
      resource[0] = RESOURCEID;
      return getEntries(fromTime, toTime, USERID, VIEW_MINE, resource, SORTBY, sortAsc, -1);
    }
  }

  public Iterator getEntries(Calendar fromTime, Calendar toTime, String USERID, boolean VIEW_MINE, int RESOURCEID[], String SORTBY, boolean sortAsc, int excludeEntryID)
  {
    try
    {
      // I want to use the Timestamp.toString() format
      java.sql.Timestamp tmp = new java.sql.Timestamp(fromTime.getTime().getTime());
      String from = tmp.toString();
      tmp.setTime(toTime.getTime().getTime());
      String to = tmp.toString();
      StringBuffer sql = new StringBuffer();
      sql.append(" where ");
      if ( VIEW_MINE )
      {
        sql.append(" (reservedby='");
        sql.append(USERID);
        sql.append("')");
      }
      if ( RESOURCEID != null)
      {
        if ( VIEW_MINE )
          sql.append(" and ");
        sql.append(" (resourceid=");
        sql.append(RESOURCEID[0]);
        if (RESOURCEID.length > 1)
        {
          for(int i=1; i < RESOURCEID.length; i++)
          {
            sql.append(" or resourceid=");
            sql.append(RESOURCEID[i]);
          }
        }
        sql.append(")");
      }

      if ( VIEW_MINE || RESOURCEID!=null)
        sql.append(" and ");

      if (excludeEntryID != -1)
      {
        sql.append("(id<>");
        sql.append(excludeEntryID);
        sql.append(") and ");
      }

      String repeatSql = sql.toString();

      sql.append(" (( fromtime >= '");
      sql.append(from);
      sql.append("' and fromtime < '");
      sql.append(to);
      sql.append("') or (totime > '");
      sql.append(from);
      sql.append("' and totime <= '");
      sql.append(to);
      sql.append("') or (fromtime <= '");
      sql.append(from);
      sql.append("' and totime >= '");
      sql.append(to);
      sql.append("'))");
      sql.append(" and (repeat=0)");
      if ( SORTBY != null && SORTBY.length()>0)
      {
        sql.append(" order by ");
        sql.append(SORTBY);
        if (sortAsc)
          sql.append(" ASC ");
        else
          sql.append(" DESC ");
      }
logger.debug(sql.toString());
      DbAdapter db = new DbAdapter(true);
      Iterator it = getBySql(sql.toString(),db);
      Vector v = new Vector();
      if ( it!= null && it.hasNext())
      {
        while (it.hasNext())
        {
          BoxReservationEntry entry = (BoxReservationEntry) it.next();
          //We need to resort resource by resource name
          if (SORTBY.compareToIgnoreCase("resourceid") == 0 )
            insertToSortedVector(entry, v, SORTBY, sortAsc, fromTime.getTimeZone());
          else
            v.addElement(entry);
        }
      }
      //Now check the repeat entries
      repeatSql += " (repeat <> 0) ";
logger.debug(repeatSql.toString());
      it = getRepeatBySql(repeatSql, db);
      if ( it != null && it.hasNext())
      {
        while (it.hasNext())
        {
          BoxReservationRepeatEntry repeat = (BoxReservationRepeatEntry) it.next();
          BoxReservationEntryBean entryBean = new BoxReservationEntryBean(new BaseIntKey(repeat.getID()),db);
          entryBean.load();

logger.debug(entryBean);
logger.debug(repeat);
          if ( isRepeatInRange(entryBean, repeat, fromTime, toTime))
          {
logger.debug("isRepeatInRange, ID= "+entryBean.getID());
            insertToSortedVector(entryBean,v,SORTBY, sortAsc, fromTime.getTimeZone());
          }
        }
      }
      db.close();
      db = null;
      return v.iterator();
    }
    catch (Exception e)
    {
      e.printStackTrace();
    //TODO: Log
    }
    return null;
  }

  public Iterator getEntries(Calendar day, String USERID, boolean VIEW_MINE, int RESOURCEID, String SORTBY, boolean sortAsc)
  {
    Calendar from = (Calendar)day.clone();
    from.set(Calendar.HOUR_OF_DAY, 0);
    from.set(Calendar.MINUTE, 0);
    from.set(Calendar.SECOND, 0);
    from.set(Calendar.MILLISECOND, 0);
    Calendar to = (Calendar)from.clone();
    to.add(Calendar.DATE,1);
    to.add(Calendar.MILLISECOND, -1);
    return getEntries(from, to, USERID, VIEW_MINE, RESOURCEID, SORTBY, sortAsc);
  }

  public Iterator getAllEntries(Calendar day, String USERID, boolean VIEW_MINE, String SORTBY, boolean sortAsc)
  {
    Calendar from = (Calendar)day.clone();
    from.set(Calendar.HOUR_OF_DAY, 0);
    from.set(Calendar.MINUTE, 0);
    from.set(Calendar.SECOND, 0);
    from.set(Calendar.MILLISECOND, 0);
    Calendar to = (Calendar)from.clone();
    to.add(Calendar.DATE,1);
    to.add(Calendar.MILLISECOND, -1);
    return getAllEntries(from, to, USERID, VIEW_MINE, SORTBY, sortAsc);
  }

  public Iterator getAllEntries(Calendar fromTime, Calendar toTime, String USERID, boolean VIEW_MINE, String SORTBY, boolean sortAsc)
  {
    return getEntries(fromTime, toTime, USERID, VIEW_MINE, -1, SORTBY, sortAsc);
  }

  public Iterator getEntriesByCat(Calendar day, String USERID, boolean VIEW_MINE, int RESOURCECATID, String SORTBY, boolean sortAsc)
  {
    Calendar from = (Calendar)day.clone();
    from.set(Calendar.HOUR_OF_DAY, 0);
    from.set(Calendar.MINUTE, 0);
    from.set(Calendar.SECOND, 0);
    from.set(Calendar.MILLISECOND, 0);
    Calendar to = (Calendar)from.clone();
    to.add(Calendar.DATE,1);
    to.add(Calendar.MILLISECOND, -1);
    return getEntriesByCat(from, to, USERID, VIEW_MINE, RESOURCECATID, SORTBY, sortAsc);
  }

  public Iterator getEntriesByCat(Calendar fromTime, Calendar toTime, String USERID, boolean VIEW_MINE, int RESOURCECATID, String SORTBY, boolean sortAsc)
  {
    int count = resourceHome.getResourceCountByCat(RESOURCECATID);
    if (count > 0)
    {
      Iterator it = resourceHome.getResourceByCat(RESOURCECATID);
      int resource[] = new int[count];
      int i=0;
      while(it.hasNext())
      {
        resource[i++] = ((BoxResourceEntry) it.next()).getID();
      }
      return getEntries(fromTime, toTime, USERID, VIEW_MINE, resource, SORTBY, sortAsc, -1);
    }
    return null;
  }

/*
  public Iterator getEntries(Calendar from, Calendar to)
  {
    BoxStatic.dateTimeFormatter.applyPattern(BoxStatic.POSTGRES_DATE_FORMAT);
    String strTime1 = BoxStatic.dateTimeFormatter.format(from.getTime());
    String strTime2 = BoxStatic.dateTimeFormatter.format(to.getTime());
    String sql = " where (( fromtime >= '" + strTime1 + "' and fromtime < '" + strTime2 + "')"
          + " or (totime >= '" + strTime1 + "' and totime < '" + strTime2 + "')"
          + " or (fromtime <= '" + strTime1 + "' and totime >= '" + strTime2 + "')) order by fromtime";
    return getBySql(sql);
  }

  public Iterator getEntries(Calendar from, Calendar to, String userID)
  {
    BoxStatic.dateTimeFormatter.applyPattern(BoxStatic.POSTGRES_DATE_FORMAT);
    String strTime1 = BoxStatic.dateTimeFormatter.format(from.getTime());
    String strTime2 = BoxStatic.dateTimeFormatter.format(to.getTime());
    String sql = " where reservedby='" + userID + "'and (( fromtime >= '" + strTime1 + "' and fromtime < '" + strTime2 + "')"
          + " or (totime >= '" + strTime1 + "' and totime < '" + strTime2 + "')"
          + " or (fromtime <= '" + strTime1 + "' and totime >= '" + strTime2 + "')) order by fromtime";
    return getBySql(sql);
  }

  public boolean hasEntry(Calendar from, Calendar to)
  {
    BoxStatic.dateTimeFormatter.applyPattern(BoxStatic.POSTGRES_DATE_FORMAT);
    String strTime1 = BoxStatic.dateTimeFormatter.format(from.getTime());
    String strTime2 = BoxStatic.dateTimeFormatter.format(to.getTime());
    String sql = " where (( fromtime >= '" + strTime1 + "' and fromtime < '" + strTime2 + "')"
          + " or (totime >= '" + strTime1 + "' and totime < '" + strTime2 + "')"
          + " or (fromtime <= '" + strTime1 + "' and totime >= '" + strTime2 + "')) order by fromtime";
    try
    {
      // Use the transaction, pass the DbAdapter to the bean
      DbAdapter db = new DbAdapter(true);
      BoxReservationEntryBean bean = new BoxReservationEntryBean(null,db);
      Iterator it = bean.findBySql(sql);
      return it.hasNext();
    }
    catch (Exception e)
    {
      e.printStackTrace();
    //TODO: Log
    }
    return false;
  }
*/

/*
  public Iterator getResourceReservationBetween(int resourceID, Date from, Date to)
  {
    BoxStatic.dateTimeFormatter.applyPattern(BoxStatic.POSTGRES_DATE_FORMAT);
    String strTime = BoxStatic.dateTimeFormatter.format(from);
    String sql = " where resourceid ='" + resourceID + "' and fromtime >= '" + strTime + "' and fromtime < '";
    strTime = BoxStatic.dateTimeFormatter.format(to);
    sql += strTime + "'";
    return getBySql(sql);
  }
*/
  protected Iterator getBySql(String sql, DbAdapter otherDb)
  {
    try
    {
      DbAdapter db = otherDb;
      if (db == null)
        db = new DbAdapter(true);
      Vector v = new Vector();

      // Use the transaction, pass the DbAdapter to the bean
      BoxReservationEntryBean bean = new BoxReservationEntryBean(null,db);
      Iterator it = bean.findBySql(sql);
      while (it.hasNext())
      {
        BaseIntKey key = (BaseIntKey) it.next();
        bean = new BoxReservationEntryBean(key,db);
        bean.load();
        BoxReservationEntry entry = new BoxReservationEntry();
        entry.copyFrom(bean);
        v.add(entry);
      }
      if (otherDb==null)
        db.close();
      return v.iterator();
    }
    catch (Exception e)
    {
      e.printStackTrace();
      //TODO: Log
    }
    return null;
  }

  protected Iterator getRepeatBySql(String sql, DbAdapter otherDb)
  {
    try
    {
      DbAdapter db = otherDb;
      if (db == null)
        db = new DbAdapter(true);

      // Use the transaction, pass the DbAdapter to the bean
      BoxReservationEntryBean bean = new BoxReservationEntryBean(null,db);
      Iterator it = bean.findBySql(sql);
      if ( it == null || (!it.hasNext()))
        return null;

      BaseIntKey key = (BaseIntKey) it.next();
      StringBuffer sql1 = new StringBuffer();
      sql1.append(" where ( id=");
      sql1.append(key.getID());
      while (it.hasNext())
      {
        key = (BaseIntKey) it.next();
        sql1.append(" or id=");
        sql1.append(key.getID());
      }
      sql1.append(")");

      Vector v = new Vector();
      // Use the transaction, pass the DbAdapter to the bean
      BoxReservationRepeatBean repeat = new BoxReservationRepeatBean(null,db);
      it = bean.findBySql(sql1.toString());
      while (it.hasNext())
      {
        key = (BaseIntKey) it.next();
        repeat = new BoxReservationRepeatBean(key,db);
        repeat.load();
        BoxReservationRepeatEntry entry = new BoxReservationRepeatEntry();
        entry.copyFrom(repeat);
        v.add(entry);
      }
    if (otherDb==null)
      db.close();
    return v.iterator();
    }
    catch (Exception e)
    {
      e.printStackTrace();
    //TODO: Log
    }
    return null;
  }

  protected void insertToSortedVector(BoxReservationEntry entry, Vector v, String SORTBY, boolean sortAsc, TimeZone tz)
  {
logger.debug("insertToSortedVector, ID= "+entry.getID());
    if (SORTBY.compareToIgnoreCase("fromtime") == 0 )
    {
      Calendar entryTime = Calendar.getInstance(tz);
      Calendar tmpTime = Calendar.getInstance(tz);
      entryTime.setTime(entry.getFromTime());
      for(int i = 0; i < v.size(); i++)
      {
        BoxReservationEntry tmp = (BoxReservationEntry)v.elementAt(i);
        tmpTime.setTime(tmp.getFromTime());
        if (( sortAsc && compareByTime(tmpTime,entryTime) >0 )
        || (!sortAsc && compareByTime(tmpTime,entryTime) < 0 ) )
        {
          v.insertElementAt(entry,i);
          return;
        }
      }
    }
    else if (SORTBY.compareToIgnoreCase("subject") == 0 )
    {
      for(int i = 0; i < v.size(); i++)
      {
        BoxReservationEntry tmp = (BoxReservationEntry)v.elementAt(i);
        if (( !sortAsc && entry.getSubject().compareToIgnoreCase(tmp.getSubject()) > 0 )
          || ( sortAsc && entry.getSubject().compareToIgnoreCase(tmp.getSubject()) < 0 ) )
        {
          v.insertElementAt(entry,i);
          return;
        }
      }
    }
    //TODO: compare by resource name
    else if (SORTBY.compareToIgnoreCase("resourceid") == 0 )
    {
      String entryRes = getResourceString(entry.getResourceID());
      for(int i = 0; i < v.size(); i++)
      {
        BoxReservationEntry tmp = (BoxReservationEntry)v.elementAt(i);
        String tmpRes = getResourceString(tmp.getResourceID());
        if ( ( !sortAsc && entryRes.compareToIgnoreCase(tmpRes) > 0 )
          || ( sortAsc && entryRes.compareToIgnoreCase(tmpRes) < 0  ) )
        {
          v.insertElementAt(entry,i);
          return;
        }
      }
    }
    else if (SORTBY.compareToIgnoreCase("reservedby") == 0 )
    {
      for(int i = 0; i < v.size(); i++)
      {
        BoxReservationEntry tmp = (BoxReservationEntry)v.elementAt(i);
        if ( ( !sortAsc && entry.getUserID().compareToIgnoreCase(tmp.getUserID()) > 0 )
          || ( sortAsc && entry.getUserID().compareToIgnoreCase(tmp.getUserID()) < 0 ) )
        {
          v.insertElementAt(entry,i);
          return;
        }
      }
    }

    //if not inserted, append to end
    v.addElement(entry);
    return;
  }

  protected boolean isRepeatInRange(BoxReservationEntry entry, BoxReservationRepeatEntry repeat, Calendar fromTime0, Calendar toTime0)
  {
//logger.debug(entry);
//logger.debug(repeat);
   // If the entry is repeat, the from and to time should be in the same day
    Calendar fromTime = (Calendar) fromTime0.clone();
    Calendar toTime = (Calendar) toTime0.clone();

    Calendar entryFrom = Calendar.getInstance(fromTime.getTimeZone());
    Calendar entryTo = Calendar.getInstance(fromTime.getTimeZone());
    entryFrom.setTime(entry.getFromTime());
    entryTo.setTime(entry.getToTime());

    Calendar untilTime = Calendar.getInstance(fromTime.getTimeZone());
    if ( repeat.getUntilTime() != null )
    {
      untilTime.setTime(repeat.getUntilTime());
    }

    if ( entryFrom.after(toTime) || untilTime.before(fromTime))
      return false;

    if (!isTimeRangeConflict(fromTime, toTime, entryFrom,entryTo))
      return false;

    int fromYear = fromTime.get(Calendar.YEAR);
    int curYear = entryFrom.get(Calendar.YEAR);
    int fromDay = fromTime.get(Calendar.DAY_OF_YEAR);
    int curDay = entryFrom.get(Calendar.DAY_OF_YEAR);

    //Assume the same day for the repeat entries
    if ( curYear != entryTo.get(Calendar.YEAR))
      entryTo.set(Calendar.YEAR, curYear);
    if ( curDay != entryTo.get(Calendar.DAY_OF_YEAR))
      entryTo.set(Calendar.DAY_OF_YEAR, curDay);

    switch (repeat.getType())
    {
      case BoxReservationRepeatEntry.BY_WEEK:
      {
  //BoxStatic.logger.debug(curDate.getTime());
        int weekdays = repeat.getDayOfWeek();
        int curDayOfWeek = getDayOfWeekBit(fromTime);
        if ( (weekdays & curDayOfWeek) == 0 )
          return false;
        int every = repeat.getEvery();
        long diff = fromTime.get(Calendar.DAY_OF_WEEK)-1;
        if (fromYear == curYear)
          diff += (curDay-fromDay);
        else
          diff += ((fromTime.getTime().getTime()/(1000*60*60*24))
              -(entryFrom.getTime().getTime()/(1000*60*60*24)));
        diff = diff / 7 ;
        if ( (diff % every) != 0 )
          return false;
        else
          return true;
      }
      case BoxReservationRepeatEntry.BY_WEEK_OF_MONTH:
      {
        // first check the same weekday
        int weekdays = repeat.getDayOfWeek();
        int curDayOfWeek = getDayOfWeekBit(fromTime);
        if ( (weekdays & curDayOfWeek) == 0 )
          return false;
        //then check every n months
        long diff = entryFrom.get(Calendar.MONTH) + 12*(curYear-fromYear) - fromTime.get(Calendar.MONTH);
        int every = repeat.getEvery();
        if ( (diff % every) != 0 )
          return false;

        //then which week
        every = repeat.getWhichDay();
        Calendar tmpCal = (Calendar)fromTime.clone();
        if ( every < 5 )
        {
          tmpCal.set(Calendar.DATE, 1);
          while ((getDayOfWeekBit(tmpCal.get(Calendar.DAY_OF_WEEK)) & weekdays) ==0)
            tmpCal.add(Calendar.DATE,1);
          diff = fromTime.get(Calendar.DATE) - tmpCal.get(Calendar.DATE);
          diff /= 7;
          if ( diff == (every-1))
            return true;
          else
            return false;
        }
        else //last week
        {
          if ( tmpCal.get(Calendar.DATE) < 21 )
            return false;
          //Add 7 days, should be different month
          int tmpMonth = tmpCal.get(Calendar.MONTH);
          tmpCal.add(Calendar.DAY_OF_YEAR, 7);
          if (  tmpMonth != tmpCal.get(Calendar.MONTH))
          {
            return true;
          }
        }
        return false;
      }
      case BoxReservationRepeatEntry.BY_MONTH:
      {
        if (fromTime.get(Calendar.DAY_OF_MONTH)==entryFrom.get(Calendar.DAY_OF_MONTH))
        {
          return true;
        }
        return false;
      }
      case BoxReservationRepeatEntry.BY_YEAR:
      {
        if (fromDay == curDay)
        {
          return true;
        }
        return false;
      }
    }
    return false;
  }

  protected boolean getNextRepeatRange(BoxReservationEntry entry,
                              BoxReservationRepeatEntry repeat,
                              Calendar curDate,
                              Calendar nextFromTime,
                              Calendar nextToTime)
  {
    Calendar untilTime = Calendar.getInstance(curDate.getTimeZone());
    if ( repeat.getUntilTime() != null )
    {
      untilTime.setTime(repeat.getUntilTime());
    }
    else
      untilTime.setTime(entry.getToTime());

    Calendar fromTime = Calendar.getInstance(curDate.getTimeZone());
    fromTime.setTime(entry.getFromTime());
    Calendar toTime = Calendar.getInstance(curDate.getTimeZone());
    toTime.setTime(entry.getToTime());
    int fromYear = fromTime.get(Calendar.YEAR);
    int fromDay = fromTime.get(Calendar.DAY_OF_YEAR);
    boolean found = false;
    while (untilTime.after(curDate))
    {
      curDate.add(Calendar.DAY_OF_YEAR,1);

      int curYear = curDate.get(Calendar.YEAR);
      int curDay = curDate.get(Calendar.DAY_OF_YEAR);
      switch (repeat.getType())
      {
        case BoxReservationRepeatBean.BY_WEEK:
        {
          int weekdays = repeat.getDayOfWeek();
          int curDayOfWeek = getDayOfWeekBit(curDate);
          if ( (weekdays & curDayOfWeek) == 0 )
            break;
          int every = repeat.getEvery();
          long diff = fromTime.get(Calendar.DAY_OF_WEEK)-1;
          if (fromYear == curYear)
            diff += (curDay-fromDay);
          else
            diff += ((curDate.getTime().getTime()/(1000*60*60*24))
                -(fromTime.getTime().getTime()/(1000*60*60*24)));
          diff = diff / 7 ;
          if ( (diff % every) == 0 )
          {
            found = true;
            break;
          }
        }
        case BoxReservationRepeatBean.BY_WEEK_OF_MONTH:
        {
          // first check the same weekday
          int weekdays = repeat.getDayOfWeek();
          int curDayOfWeek = getDayOfWeekBit(curDate);
          if ( (weekdays & curDayOfWeek) == 0 )
            break;
          //then check every n months
          long diff = curDate.get(Calendar.MONTH) + 12*(curYear-fromYear) - fromTime.get(Calendar.MONTH);
          int every = repeat.getEvery();
          if ( (diff % every) != 0 )
            break;

          //then which week
          every = repeat.getWhichDay();
          Calendar tmpCal = (Calendar)curDate.clone();
          if ( every < 5 )
          {
            tmpCal.set(Calendar.DATE, 1);
            while ((getDayOfWeekBit(tmpCal.get(Calendar.DAY_OF_WEEK)) & weekdays) ==0)
              tmpCal.add(Calendar.DATE,1);
            diff = curDate.get(Calendar.DATE) - tmpCal.get(Calendar.DATE);
            diff /= 7;
            if ( diff == (every-1))
              found = true;
          }
          else //last week
          {
            if ( tmpCal.get(Calendar.DATE) > 21 )
            {
            //Add 7 days, should different month
              tmpCal.add(Calendar.DAY_OF_YEAR, 7);
              if (tmpCal.get(Calendar.MONTH) != curDate.get(Calendar.MONTH))
                found = true;
            }
          }
          break;
        }
        case BoxReservationRepeatBean.BY_MONTH:
        {
          if (fromTime.get(Calendar.DAY_OF_MONTH)==curDate.get(Calendar.DAY_OF_MONTH))
            found = true;
          break;
        }
        case BoxReservationRepeatBean.BY_YEAR:
        {
          if (fromDay == curDay)
            found = true;
          break;
        }
      }
      if (found)
      {
        nextFromTime.set(Calendar.YEAR, curYear);
        nextFromTime.set(Calendar.DAY_OF_YEAR, curDay);
        nextFromTime.set(Calendar.HOUR_OF_DAY, fromTime.get(Calendar.HOUR_OF_DAY));
        nextFromTime.set(Calendar.MINUTE, fromTime.get(Calendar.MINUTE));
        nextFromTime.set(Calendar.SECOND, 0);
        nextFromTime.set(Calendar.MILLISECOND, 0);
        nextToTime.set(Calendar.YEAR, curYear);
        nextToTime.set(Calendar.DAY_OF_YEAR, curDay);
        nextToTime.set(Calendar.HOUR_OF_DAY, toTime.get(Calendar.HOUR_OF_DAY));
        nextToTime.set(Calendar.MINUTE, toTime.get(Calendar.MINUTE));
        nextToTime.set(Calendar.SECOND, 0);
        nextToTime.set(Calendar.MILLISECOND, 0);
        return true;
      }
    }
    return false;
  }

  protected boolean checkConflict(Calendar fromtime, Calendar totime, int resourceID, int entryID)
  {
    int resource[] = new int [1];
    resource[0] = resourceID;

    Iterator it = getEntries(fromtime, totime, null, false, resource, "fromtime", true, entryID);
//logger.debug("After get conflict entries. ");
//logger.debug("it="+it + "  next=" + it.hasNext());
    if ( it != null)
    {
      while ( it.hasNext())
      {
        BoxReservationEntry tmp = (BoxReservationEntry)it.next();
        if (tmp.getID() != entryID)
        {
logger.debug("Conflict with: ");
logger.debug(tmp);
          return true;
        }
      }
    }
    return false;
  }

  protected boolean isTimeRangeConflict(Calendar t1, Calendar t2,
                        Calendar t3, Calendar t4)
  {
//logger.debug(new java.sql.Timestamp(t1.getTime().getTime()));
//logger.debug(new java.sql.Timestamp(t2.getTime().getTime()));
//logger.debug(new java.sql.Timestamp(t3.getTime().getTime()));
//logger.debug(new java.sql.Timestamp(t4.getTime().getTime()));

    long tt1 = t1.get(Calendar.HOUR_OF_DAY)*60*60 + t1.get(Calendar.MINUTE)*60 + t1.get(Calendar.SECOND);
    long tt2 = t2.get(Calendar.HOUR_OF_DAY)*60*60 + t2.get(Calendar.MINUTE)*60 + t2.get(Calendar.SECOND);
    long tt3 = t3.get(Calendar.HOUR_OF_DAY)*60*60 + t3.get(Calendar.MINUTE)*60 + t3.get(Calendar.SECOND);
    long tt4 = t4.get(Calendar.HOUR_OF_DAY)*60*60 + t4.get(Calendar.MINUTE)*60 + t4.get(Calendar.SECOND);
    if ( ( tt1 >= tt3 && tt1 < tt4)
        || ( tt2 > tt3 && tt2 <= tt4)
        ||( tt1 < tt3 && tt4 < tt2 ))
        return true;
      return false;
/*

    if ( ( compareByTime(t1, t3) >= 0
            && compareByTime(t1, t4) < 0)
        ||( compareByTime(t2, t3) > 0
            && compareByTime(t2, t4) <= 0)
        ||( compareByTime(t1, t3) < 0
            && compareByTime(t4, t2) < 0 ))
        return true;
      return false;
      }
      */
   }

  protected int compareByTime(Calendar time1, Calendar time2)
  {
    int i = time1.get(Calendar.HOUR_OF_DAY)- time2.get(Calendar.HOUR_OF_DAY);
    if ( i != 0 )
      return i;
    i = time1.get(Calendar.MINUTE) - time2.get(Calendar.MINUTE);
    if ( i != 0)
      return i;
    return time1.get(Calendar.SECOND) - time2.get(Calendar.SECOND);
  }

  public int getDayOfWeekBit(int weekday)
  {
    switch (weekday)
    {
      case Calendar.SUNDAY:
        return 1;
      case Calendar.MONDAY:
        return 2;
      case Calendar.TUESDAY:
        return 4;
      case Calendar.WEDNESDAY:
        return 8;
      case Calendar.THURSDAY:
        return 16;
      case Calendar.FRIDAY:
        return 32;
      case Calendar.SATURDAY:
        return 64;
    }
    return 1;
  }

  public int getDayOfWeekBit(Calendar curDate)
  {
    return getDayOfWeekBit(curDate.get(Calendar.DAY_OF_WEEK));
  }

  //TODO: Share with Calendar
  public void getMonthStartEnd(Calendar start, Calendar end, Calendar currentDate)
  {
    start.setTime(currentDate.getTime());
    end.setTime(start.getTime());

    start.set(Calendar.DATE,1);
    int dayOfWeek = start.get(Calendar.DAY_OF_WEEK)-1;
    start.add(Calendar.DAY_OF_YEAR,-dayOfWeek);

    end.add(Calendar.MONTH, 1);
    end.set(Calendar.DATE,1);
    end.add(Calendar.DATE,-1);
    dayOfWeek = end.get(Calendar.DAY_OF_WEEK);
    end.add(Calendar.DAY_OF_YEAR,(7-dayOfWeek));
  }

  public String getEntryString(BoxReservationEntry entry, String SORTBY, TimeZone tz)
  {
    StringBuffer str = new StringBuffer();
    if (SORTBY.compareToIgnoreCase("resourceid") == 0)
    {
      str.append(getResourceString(entry.getResourceID()));
      str.append(" ");
      str.append(entry.getTimeRangeString(tz));
      str.append(" ");
      str.append(entry.getSubject());
      str.append(" ");
      str.append(entry.getUserID());
    }
    else if (SORTBY.compareToIgnoreCase("subject") == 0)
    {
      str.append(entry.getSubject());
      str.append(" ");
      str.append(getResourceString(entry.getResourceID()));
      str.append(" ");
      str.append(entry.getTimeRangeString(tz));
      str.append(" ");
      str.append(entry.getUserID());
    }
    else if (SORTBY.compareToIgnoreCase("reservedby") == 0)
    {
      str.append(entry.getUserID());
      str.append(" ");
      str.append(getResourceString(entry.getResourceID()));
      str.append(" ");
      str.append(entry.getTimeRangeString(tz));
      str.append(" ");
      str.append(entry.getSubject());
    }
    else
    {
      //default is sort by fromtime
      str.append(entry.getTimeRangeString(tz));
      str.append(" ");
      str.append(entry.getUserID());
      str.append(" ");
      str.append(getResourceString(entry.getResourceID()));
      str.append(" ");
      str.append(entry.getSubject());
    }

    return str.toString();
  }
}
